home *** CD-ROM | disk | FTP | other *** search
- /*===================================================================
- *
- * FILE:
- * prs2append.c
- *
- * IDENTIFICATION:
- * $Header: /private/postgres/src/rules/prs2/RCS/prs2append.c,v 1.14 1991/07/16 13:14:16 sp Exp $
- *
- * DESCRIPTION:
- *
- *
- *===================================================================
- */
-
- #include "tmp/c.h"
- #include "access/heapam.h"
- #include "access/htup.h"
- #include "storage/buf.h"
- #include "utils/rel.h"
- #include "utils/log.h"
- #include "utils/palloc.h"
- #include "rules/prs2.h"
- #include "rules/prs2stub.h"
- #include "nodes/execnodes.h" /* which includes access/rulescan.h */
- #include "parser/parse.h" /* for the APPEND */
-
- /*-------------------------------------------------------------------
- *
- * prs2Append
- */
- Prs2Status
- prs2Append(prs2EStateInfo, relationRuleInfo, explainRelation,
- tuple, buffer, relation, returnedTupleP, returnedBufferP)
- Prs2EStateInfo prs2EStateInfo;
- RelationRuleInfo relationRuleInfo;
- Relation explainRelation;
- HeapTuple tuple;
- Buffer buffer;
- Relation relation;
- HeapTuple *returnedTupleP;
- Buffer *returnedBufferP;
- {
- RuleLock locks, newLocks, explocks, temp;
- Prs2Stub stubs;
- AttributeValues attrValues;
- AttributeNumber numberOfAttributes, attrNo;
- bool insteadRuleFound;
- Name relName;
- Prs2Status status;
- int i;
- TupleDescriptor tupDesc;
-
- /*
- * NOTE: currently we only have relation-level-locks for
- * "on append ...." rules.
- * So we do not check for any tuple level locks.
- * (after all this is a brand new tuple with no locks on it)
- */
- relName = RelationGetRelationName(relation);
- tupDesc = RelationGetTupleDescriptor(relation);
-
- locks = relationRuleInfo->relationLocks;
- stubs = relationRuleInfo->relationStubs;
-
- /*
- * now extract from the tuple the array of the attribute values.
- */
- attrValues = attributeValuesCreate(tuple, buffer, relation);
-
- /*
- * activate 'on append' rules...
- */
- if (locks == NULL || prs2GetNumberOfLocks(locks)==0) {
- /*
- * the trivial case: no "on append" rules!
- */
- status = PRS2_STATUS_TUPLE_UNCHANGED;
- insteadRuleFound = false;
- *returnedTupleP = tuple;
- *returnedBufferP = buffer;
- } else {
- /*
- * first calculate all the attributes of the tuple.
- * I.e. see if there is any rule that will calculate a new
- * value for a tuple's attribute (i.e. a backward chaining rule).
- */
- numberOfAttributes = RelationGetNumberOfAttributes(relation);
- for (attrNo=1; attrNo <= numberOfAttributes; attrNo++) {
- prs2ActivateBackwardChainingRules(
- prs2EStateInfo,
- explainRelation,
- relation,
- attrNo,
- PRS2_NEW_TUPLE,
- InvalidObjectId,
- InvalidAttributeValues,
- InvalidRuleLock,
- LockTypeInvalid,
- tuple->t_oid,
- attrValues,
- locks,
- LockTypeAppendWrite,
- (AttributeNumberPtr) NULL,
- (AttributeNumber) 0);
- }
-
- /*
- * now, try to activate all other 'on append' rules.
- * (these ones will have a LockTypeAppendAction lock ....)
- * NOTE: the attribute number stored in the locks for these
- * rules must be `InvalidAttributeNumber' because they
- * do not refer to any attribute in particular.
- */
- prs2ActivateForwardChainingRules(
- prs2EStateInfo,
- explainRelation,
- relation,
- InvalidAttributeNumber,
- LockTypeAppendAction,
- PRS2_NEW_TUPLE,
- InvalidObjectId,
- InvalidAttributeValues,
- InvalidRuleLock,
- LockTypeInvalid,
- tuple->t_oid,
- attrValues,
- locks,
- LockTypeAppendWrite,
- &insteadRuleFound,
- (AttributeNumberPtr) NULL,
- (AttributeNumber) 0);
-
- /*
- * Now all the correct values for the new tuple
- * (the ones proposed by the user + the ones calculated
- * by rule with 'LockTypeAppendWrite' locks,
- * are in the attrValues array.
- * Create a new tuple with the correct values.
- */
- if (attributeValuesMakeNewTuple(
- tuple, buffer,
- attrValues, locks,
- LockTypeRetrieveWrite,
- relation, returnedTupleP)) {
- status = PRS2_STATUS_TUPLE_CHANGED;
- *returnedBufferP = InvalidBuffer;
- } else {
- status = PRS2_STATUS_TUPLE_UNCHANGED;
- *returnedTupleP = tuple;
- *returnedBufferP = buffer;
- }
- }
-
-
- /*
- * OK, if there was an 'instead' rule, then we must not
- * append the tuple.
- */
- if (insteadRuleFound) {
- return(PRS2_STATUS_INSTEAD);
- }
-
- /*
- * well, there was no instead rule.
- * Check all the stubs to see if this tuple should
- * inherit some new locks.
- */
- newLocks = prs2FindLocksForNewTupleFromStubs(
- *returnedTupleP,
- *returnedBufferP,
- stubs,
- relation);
- /*
- * Check if any "export" lock should be added to the
- * tuple. If yes, then activate the appropriate
- * rule plans...
- */
- explocks = prs2FindNewExportLocksFromLocks(newLocks);
- for (i=0; i<prs2GetNumberOfLocks(explocks); i++) {
- Prs2OneLock oneLock;
- AttributeNumber attrno;
- oneLock = prs2GetOneLockFromLocks(explocks,i);
- attrno = prs2OneLockGetAttributeNumber(oneLock);
- if (!attrValues[attrno-1].isNull)
- prs2ActivateExportLockRulePlan(oneLock,
- attrValues[attrno-1].value,
- tupDesc->data[attrno-1]->atttypid,
- APPEND);
- }
-
- /*
- * the locks that this tuple must get is the union of 'newLocks'
- * and 'explocks'.
- */
- temp = prs2LockUnion(newLocks, explocks);
- prs2FreeLocks(newLocks);
- prs2FreeLocks(explocks);
- newLocks = temp;
-
- attributeValuesFree(attrValues, relation);
-
- /*
- * Did the locks or an attribute of the tuple change?
- * If yes, whistle to the executor...
- */
- if (status == PRS2_STATUS_TUPLE_CHANGED) {
- if (prs2GetNumberOfLocks(newLocks) == 0) {
- prs2FreeLocks(newLocks);
- return(PRS2_STATUS_TUPLE_CHANGED);
- } else {
- HeapTupleSetRuleLock(*returnedTupleP, InvalidBuffer, newLocks);
- return(PRS2_STATUS_TUPLE_CHANGED);
- }
- } else { /* status = PRS2_STATUS_TUPLE_UNCHANGED */
- if (prs2GetNumberOfLocks(newLocks) == 0) {
- prs2FreeLocks(newLocks);
- return(PRS2_STATUS_TUPLE_UNCHANGED);
- } else {
- /*
- * we must create a COPY of the tuple!
- */
- *returnedTupleP = palloctup(tuple,buffer,relation);
- HeapTupleSetRuleLock(*returnedTupleP, InvalidBuffer, newLocks);
- *returnedBufferP = InvalidBuffer;
- return(PRS2_STATUS_TUPLE_CHANGED);
- }
- }
- }
-
- /*-----------------------------------------------------------------------
- * prs2FindLocksForNewTupleFromStubs
- *
- * Given a brand new tuple that will be inserted in a relation,
- * check all the rule stubs to find what tuple-level locks this tuple
- * should get.
- *
- * NOTE: the following code can be hazardus to your mental health...
- * (but if you are hacking it you have already reached the point of no
- * return, so...)
- *
- * The idea is that we have a new tuple, some stubs and we want to see
- * which of the stubs' qualifications are satisfied by the tuple.
- * So, all we need is to test if the tuple satisfies the stub's
- * qualification, eh? Trivial! Easy! 5 lines of code!
- * NOOOOOOO! This is NOT enough! The problem is that a stub's
- * qualification must also be considered "satisfied" if it depends on
- * an attribute with a "write" lock.
- * So, for every new "write" lock added, we must re-check all the
- * previous stubs to see if they "depend" on this lock. Bliah!
- *
- *-----------------------------------------------------------------------
- */
- RuleLock
- prs2FindLocksForNewTupleFromStubs(tuple, buffer, stubs, relation)
- HeapTuple tuple;
- Buffer buffer;
- Prs2Stub stubs;
- Relation relation;
- {
- TupleDescriptor tdesc;
- Prs2OneStub thisStub;
- LispValue thisQual;
- Prs2OneLock thisLock;
- Prs2LockType locktype;
- bool done;
- RuleLock res, t;
- bool *used;
- register int i, j;
-
- /*
- * check the trival case...
- */
- if (stubs->numOfStubs == 0) {
- res = prs2MakeLocks();
- return(res);
- }
-
- /*
- * keep an array of the stubs that "succeeded" in adding
- * locks to the tuple.
- */
- used = (bool *) palloc(stubs->numOfStubs * sizeof(bool));
- if (used == NULL) {
- elog(WARN,"prs2FindLocksForNewTupleFromStubs: out of memory");
- }
- for (i=0; i<stubs->numOfStubs; i++)
- used[i] = false;
-
- tdesc = RelationGetTupleDescriptor(relation);
- res = prs2MakeLocks();
-
- /*
- * Yes, I know, this code sure looks ugly, but it must work
- * by tonight! When (if!) I have some more time I'll clean it
- * & speed it up a little...
- */
- do {
- done = true;
- /*
- * check one by one all stubs...
- */
- for (i=0; i<stubs->numOfStubs; i++) {
- if (used[i]) {
- /*
- * no need to check this stub again
- */
- continue;
- }
- thisStub = stubs->stubRecords[i];
- thisQual = thisStub->qualification;
- /*
- * should we use the lock of this stub?
- * The answer is yes iff the tuple satisfies
- * the stub's qual or if the qual depends
- * on an attribute which has a "write" lock on it.
- */
- if (prs2StubQualTestTuple(tuple, buffer, tdesc, thisQual)) {
- used[i] = true;
- } else {
- for (j=0; j<prs2GetNumberOfLocks(res); j++) {
- thisLock = prs2GetOneLockFromLocks(res, j);
- locktype = prs2OneLockGetLockType(thisLock);
- if (locktype == LockTypeRetrieveWrite) {
- /*
- * does this stub depend on the attribute
- * "calculated" by this "write" lock?
- */
- if (prs2DoesStubDependsOnAttribute(thisStub,
- prs2OneLockGetAttributeNumber(thisLock))){
- used[i] = true;
- break;
- }
- }
- }
- }
- if (used[i]) {
- /*
- * OK, we must use this attribute.
- * Add its lock to the 'res'
- */
- t = prs2LockUnion(res, thisStub->lock);
- prs2FreeLocks(res);
- res = t;
- /*
- * if this stub had a "write" lock, then
- * 'done' is false because we must check again
- * all the stubs.
- */
- for (j=0; j<prs2GetNumberOfLocks(thisStub->lock); j++) {
- thisLock = prs2GetOneLockFromLocks(thisStub->lock, j);
- locktype = prs2OneLockGetLockType(thisLock);
- if (locktype == LockTypeRetrieveWrite) {
- done = false;
- break;
- }
- }
- }
- }
- } while (!done);
-
- return(res);
- }
-